package org.vaadin.dotmap;

import java.util.LinkedList;
import java.util.List;

import org.vaadin.dotmap.client.ui.VDotMap;
import org.vaadin.dotmap.client.ui.VDotMap.Highlight;

import com.vaadin.terminal.ClassResource;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.ui.AbstractComponent;

/**
 * Shows a map of the world consisting of 'punched out' (transparent) holes in a
 * solid background. Highlight spots can be added behind the map image, so that
 * the dots seem to light up. Alternatively, highlights can be added on top of
 * the map image. Highlight fade away after a configurable delay, and both the
 * map image and the highlight image can be customized. The map dot color can
 * also easily be set.
 */
@com.vaadin.ui.ClientWidget(org.vaadin.dotmap.client.ui.VDotMap.class)
public class DotMap extends AbstractComponent {

    private final String DEFAULT_MAP = "dotmap.gif";
    private final String DEFAULT_HIGHLIGHT = "white.png";

    private Resource mapImage;
    private Resource highlightImage;
    private int topCrop = 0;
    private List<Highlight> higlights;
    private int fadeMsec = -1;
    private String dotColor = "#000";

    public DotMap() {

    }

    public DotMap(String caption) {
        setCaption(caption);
    }

    public Resource getMapImage() {
        return mapImage;
    }

    /**
     * Set image to use as map image. The map must be a Mercator projection,
     * with the exception that it can be cropped from the top - see
     * {@link #setTopCrop(int)}.
     * 
     * @param mapImage
     */
    public void setMapImage(Resource mapImage) {
        this.mapImage = mapImage;
        requestRepaint();
    }

    public String getDotColor() {
        return dotColor;
    }

    /**
     * The color used for the dots, i.e the 'punched out' (transparent) holes in
     * the map image, as a HTML color string (e.g "red", "#ff0000", "#f00");
     * 
     * @param color
     */
    public void setDotColor(String color) {
        this.dotColor = color;
        requestRepaint();
    }

    public int getTopCrop() {
        return topCrop;
    }

    /**
     * Sets how many pixels are missing from the top of the Mercator projection.
     * This enables you to leave out some areas where not a lot of stuff is
     * going on usually. To crop the bottom, use {@link #setHeight(String)}.
     * 
     * @param croppedPixels
     */
    public void setTopCrop(int croppedPixels) {
        this.topCrop = croppedPixels;
        requestRepaint();
    }

    public Resource getHighlightImage() {
        return highlightImage;
    }

    /**
     * Set the image used by default as highlight image. You can also choose a
     * custom image when showing each highlight.
     * 
     * @param highlightImage
     */
    public void setHighlightImage(Resource highlightImage) {
        this.highlightImage = highlightImage;
        requestRepaint();
    }

    /**
     * Shows a highlight at the specified position, using the default highlight
     * image, fade time, and z-index.
     * 
     * @param lat
     * @param lon
     */
    public void showHighlight(double lat, double lon) {
        if (this.higlights == null) {
            this.higlights = new LinkedList<Highlight>();
        }
        this.higlights.add(new Highlight(lat, lon));
        requestRepaint();
    }

    /**
     * Shows a highlight at the specified position, using the specified
     * highlight image, fade time, and z-index.
     * 
     * @param lat
     * @param lon
     */
    public void showHighlight(Highlight highlight) {
        if (this.higlights == null) {
            this.higlights = new LinkedList<Highlight>();
        }
        this.higlights.add(highlight);
        requestRepaint();
    }

    public int getFadeMsec() {
        return fadeMsec;
    }

    /**
     * Sets the default fade time for highlights. You can also choose a custom
     * fade time when showing each highlight.
     * 
     * @param fadeMsec
     */
    public void setFadeMsec(int fadeMsec) {
        this.fadeMsec = fadeMsec;
        requestRepaint();
    }

    @Override
    public void paintContent(PaintTarget target) throws PaintException {
        super.paintContent(target);

        if (this.fadeMsec > -1) {
            target.addAttribute(VDotMap.ATTR_FADE_MSEC, this.fadeMsec);
        }

        if (this.mapImage == null) {
            this.mapImage = new ClassResource(getClass(), DEFAULT_MAP,
                    getApplication());
        }
        target.addAttribute(VDotMap.ATTR_MAP_IMAGE, this.mapImage);

        target.addAttribute(VDotMap.ATTR_MAP_COLOR, this.dotColor);

        target.addAttribute(VDotMap.ATTR_MAP_TOP_CROP, this.topCrop);

        if (this.highlightImage == null) {
            this.highlightImage = new ClassResource(getClass(),
                    DEFAULT_HIGHLIGHT, getApplication());
        }
        target.addAttribute(VDotMap.ATTR_HIGHLIGHT_IMAGE, this.highlightImage);

        if (this.higlights != null) {
            for (Highlight h : this.higlights) {
                target.startTag(VDotMap.TAG_HIGHLIGHT);
                target.addAttribute("lat", h.getLatitude());
                target.addAttribute("lon", h.getLongitude());
                if (h.getImg() != null) {
                    target.addAttribute(VDotMap.ATTR_HIGHLIGHT_IMAGE,
                            (Resource) h.getImg());
                }
                if (h.getFadeMsec() > -1) {
                    target.addAttribute(VDotMap.ATTR_FADE_MSEC, h.getFadeMsec());
                }
                if (h.getZIndex() != VDotMap.ZINDEX_UNDER) {
                    target.addAttribute(VDotMap.ATTR_HIGHLIGHT_ZINDEX,
                            h.getZIndex());
                }
                target.endTag(VDotMap.TAG_HIGHLIGHT);
            }
            this.higlights = null;
        }
    }
}
